Exercise 1: ggplot

The following url: “http://cdiac.ess-dive.lbl.gov/ftp/ndp030/CSV-FILES/nation.1751_2014.csv” contains data on fossil fuel emissions.

  1. Read data to R. Note that rows 1-3 contain information on the dataset itself. Delete these rows as they do not contain relevant information.
emissions <- read.csv("http://cdiac.ess-dive.lbl.gov/ftp/ndp030/CSV-FILES/nation.1751_2014.csv")
emissions <- emissions[4:nrow(emissions), ]
head(emissions)
  1. Compute the total yearly \(CO_2\) emissions (column “Total.CO2.emissions.from.fossil.fuels.and.cement.production..thousand.metric.tons.of.C.”) summed over all countries (the world total \(CO_2\) emission). You can use a forloop over “years” or dplyr functions.
library(dplyr)
emissionsTotal <- emissions %>% 
  group_by(Year) %>%
  summarise(
    world_total = 
      sum(Total.CO2.emissions.from.fossil.fuels.and.cement.production..thousand.metric.tons.of.C.))
  1. Plot the world (summed over all countries) \(CO_2\) emission over time in billion tonnes (Gt) per year, i.e. divide the quantity computed in (b) by 10^6. You can use a line or a scatter plot.
library(ggplot2)
ggplot(emissionsTotal, aes(x = Year, y = world_total/10^6)) +
  geom_line() + theme_bw()

  1. Now read the dataset located at “https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/all/all.csv” which contains an assignment of countries to regions. Merge emissions dataset to the countries dataset. Note that in emissions dataset countries are given with all caps, but not in countries dataset. You need to change that before merging the two tables. Hint: use the function toupper(). Add a column ‘co2_emission’ equal to \(CO_2\) emission in Gt, i.e. ‘Total.CO2.emissions.from.fossil.fuels.and.cement.production..thousand.metric.tons.of.C.’/10^6
countries <- read.csv("https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/all/all.csv")
countries$name <- toupper(countries$name)
head(countries)
df <- emissions %>% 
  left_join(countries, by = c("Nation" = "name")) %>%
  mutate(co2_emission = Total.CO2.emissions.from.fossil.fuels.and.cement.production..thousand.metric.tons.of.C./10^6)
Column `Nation`/`name` joining factor and character vector, coercing into character vector
head(df)
  1. Use dplyr to compute total annual \(CO_2\) (‘co2_emission’) emission per ‘sub.region’.
df <- df %>%
  group_by(Year, sub.region) %>%
  summarise(co2_emission = sum(co2_emission))
head(df)
  1. Use ggplot to generate a stacked density plot the annual \(CO_2\) (in giga tonnes) by world regions (“sub.region”). Your plot should resemble something like this (but with other regional categories, and slightly different values). Hint: use geom_area() function with suitable parameters.
ggplot(df, aes(fill = sub.region, x = Year, y = co2_emission)) +
  geom_area(stat = "identity") +
  theme(legend.position = "bottom")

Exercise 2: Gene expression data

In this exercise we will use the DNA microarray gene expression data. You can read more about it on page 5 of “The Elements of Statistical Learning”.

microarray <- read.table("https://web.stanford.edu/~hastie/ElemStatLearn/datasets/nci.data")
info <- read.table("https://web.stanford.edu/~hastie/ElemStatLearn/datasets/nci.info.txt",
                   skip = 12)
colnames(microarray) <- info$V1
microarray <- as.matrix(microarray)
microarray[1:5, 1:5]
        CNS      CNS    CNS  RENAL BREAST
[1,]  0.300 0.679961  0.940  0.280  0.485
[2,]  1.180 1.289961 -0.040 -0.310 -0.465
[3,]  0.550 0.169961 -0.170  0.680  0.395
[4,]  1.140 0.379961 -0.040 -0.810  0.905
[5,] -0.265 0.464961 -0.605  0.625  0.200

In the ‘microarray’ matrix columns correspond to samples, and rows to genes.

  1. Randomly subset microarray to 500 genes. Then, plot a heatmap without clustering/dendrograms. You can use ‘asp = 0.2’ argument to change the aspect ratio of the heatmap.
set.seed(123456)
idx <- sample(1:500)
heatmap(microarray[idx, ], Rowv = NA, Colv = NA, asp=0.2)

  1. Plot the previous heatmap with red/green color scheme. For your convenience here is the color vector you might like to use:
redgreen <- c("#FF0000", "#DB0000", "#B60000", "#920000", "#6D0000",
              "#490000", "#240000", "#000000", "#002400", "#004900",
              "#006D00", "#009200", "#00B600", "#00DB00", "#00FF00")
heatmap(microarray[idx, ], Rowv = NA, Colv = NA, asp=0.2, col = redgreen)

Now, plot the same graph but with dendrogram for rows (rows clustering).

heatmap(microarray[idx, ], Colv = NA, asp=0.2, col = redgreen)

  1. Now instead of base heatmap, use interactive heatmaply() to generate the previous plot. You might want to add a command similar to the following %>% layout(margin = list(l = 150, b = 350), autosize = F, width = 600, height = 800) to the plot to set margins and to resize it.
library(heatmaply)
heatmaply(data.frame(microarray[idx, ]), Colv = FALSE, colors = redgreen) %>%
  layout(margin = list(l = 150, b = 350), autosize = F, width = 600, height = 800)
  1. What interesting patterns do you observe? Are there some differences between conditions? Are some genes up down regulated for certain groups? No need for long answers just look at the heatmap state what you see.

Exercise 3: Hypothesis testing

Recall the movies data-frame we used in for lecture 3 exercises. It contains information on movies from the last three decates, which was scrapped from the IMDB database.

url <- "https://raw.githubusercontent.com/Juanets/movie-stats/master/movies.csv"
movies <- tbl_df(read.csv(url))
movies
  1. Generate a boxplot of runtimes for action movies and commedies with jittered points overlaid on top. You might consider setting collor, fill and alpha arguments to modify clarity and transparency of the plot.
ggplot(movies %>% filter(genre %in% c("Action", "Comedy")), 
       aes(x = genre, y = runtime)) +
  geom_boxplot() +
  geom_jitter(height = 0,alpha = 0.2, color = "grey30") 

  1. Test a hypothesis that the action movies have higher mean runtime (length) than the comedies. Is the difference statistically greater than zero at significance level \(\alpha = 0.05\)?
t.test(formula = runtime ~ genre,
       data = movies %>% 
         filter(genre %in% c("Action", "Comedy")), 
       alternative = "greater")

Exercise 4: linear model

  1. Read the data from “http://www-bcf.usc.edu/~gareth/ISL/Advertising.csv” containing information on sales of a product and the amount spent on advertising using different media channels.
  1. Generate a scatterplot of sales against the amount of TV advertising and add a linear fit line.

  1. Now make a 3D scatterplot with axes corresponding to ‘sales’, ‘TV’ and ‘radio’.
  1. The dataset has 200 rows. Divide it into a train set with 150 observations and a test set with 50 observations, i.e. use sample(1:200, n = 150) to randomly choose row indices of the advertising dataset to include in the train set. The remaining indices should be used for the test set. Remember to choose and set the seed for randomization!
set.seed(12345)
idx <- sample(1:nrow(advertising), 150)
train <- advertising[idx, ]
test <- advertising[-idx, ]
  1. Fit a linear model to the training set, where the sales values are predicted by the amount of TV advertising. Print the summary of the fitted model. Then, predict the sales values for the test set and evaluate the test model accuracy in terms of mean squared error (MSE).
fit1 <- lm(sales ~ TV, data  = train)
summary(fit1)

Call:
lm(formula = sales ~ TV, data = train)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.5442 -1.9976 -0.0122  1.9375  7.4951 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 7.236487   0.529751   13.66   <2e-16 ***
TV          0.045090   0.003164   14.25   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.266 on 148 degrees of freedom
Multiple R-squared:  0.5785,    Adjusted R-squared:  0.5756 
F-statistic: 203.1 on 1 and 148 DF,  p-value: < 2.2e-16
yhat <- predict(fit1, test)
(mse1 <- mean((test$sales - yhat)^2))
[1] 10.75636
  1. Fit a multiple linerar regression model including all the variables ‘TV’, ‘radio’, ‘newspaper’ to model the ‘sales’ in the training set. Then, compute the predicted sales for the test set with the new model and evalued the MSE.
    Did the error decrease from the one correspodning to the previous model?
fit2 <- lm(sales ~ TV + radio + newspaper, data  = train)
summary(fit2)

Call:
lm(formula = sales ~ TV + radio + newspaper, data = train)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.7941 -0.9224  0.2934  1.1735  2.5006 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 2.796684   0.365948   7.642 2.61e-12 ***
TV          0.045314   0.001624  27.908  < 2e-16 ***
radio       0.190504   0.009962  19.124  < 2e-16 ***
newspaper   0.002500   0.006671   0.375    0.708    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.676 on 146 degrees of freedom
Multiple R-squared:  0.8905,    Adjusted R-squared:  0.8883 
F-statistic: 395.9 on 3 and 146 DF,  p-value: < 2.2e-16
yhat <- predict(fit2, test)
(mse2 <- mean((test$sales - yhat)^2))
[1] 2.98811
  1. Look at the summary output for the multiple regression model and note which of the coefficient in the model is significant. Are all of them significant? If not refit the model including only the features found significant. Which of the models should you choose?
fit3 <- lm(sales ~ TV + radio, data  = train)
summary(fit3)

Call:
lm(formula = sales ~ TV + radio, data = train)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.8650 -0.9098  0.2910  1.1932  2.5064 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 2.840365   0.345867   8.212 1.02e-13 ***
TV          0.045326   0.001619  28.002  < 2e-16 ***
radio       0.191739   0.009373  20.457  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.671 on 147 degrees of freedom
Multiple R-squared:  0.8904,    Adjusted R-squared:  0.8889 
F-statistic: 597.3 on 2 and 147 DF,  p-value: < 2.2e-16
yhat <- predict(fit3, test)
(mse3 <- mean((test$sales - yhat)^2))
[1] 2.962326

Exercise 4: classification

Email spam ESL

Exercise 5: clustering

LS0tCnRpdGxlOiAiSG9tZXdvcmsgMiIKYXV0aG9yOiAiTGFuIEh1b25nIE5ndXllbiIKZGF0ZTogIk9jdG9iZXIgMjQsIDIwMTciCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgcGRmX2RvY3VtZW50OgogICAgbnVtYmVyX3NlY3Rpb25zOiBubwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpybShsaXN0ID0gbHMoKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCgojIEV4ZXJjaXNlIDE6IGBnZ3Bsb3RgCgpUaGUgZm9sbG93aW5nIHVybDoKImh0dHA6Ly9jZGlhYy5lc3MtZGl2ZS5sYmwuZ292L2Z0cC9uZHAwMzAvQ1NWLUZJTEVTL25hdGlvbi4xNzUxXzIwMTQuY3N2Igpjb250YWlucyBkYXRhIG9uIGZvc3NpbCBmdWVsIGVtaXNzaW9ucy4KCmEuIFJlYWQgZGF0YSB0byBSLiBOb3RlIHRoYXQgcm93cyAxLTMgY29udGFpbiBpbmZvcm1hdGlvbiBvbiB0aGUgZGF0YXNldCBpdHNlbGYuCkRlbGV0ZSB0aGVzZSByb3dzIGFzIHRoZXkgZG8gbm90IGNvbnRhaW4gcmVsZXZhbnQgaW5mb3JtYXRpb24uCgpgYGB7cn0KZW1pc3Npb25zIDwtIHJlYWQuY3N2KCJodHRwOi8vY2RpYWMuZXNzLWRpdmUubGJsLmdvdi9mdHAvbmRwMDMwL0NTVi1GSUxFUy9uYXRpb24uMTc1MV8yMDE0LmNzdiIpCmVtaXNzaW9ucyA8LSBlbWlzc2lvbnNbNDpucm93KGVtaXNzaW9ucyksIF0KaGVhZChlbWlzc2lvbnMpCmBgYAoKYi4gQ29tcHV0ZSB0aGUgdG90YWwgeWVhcmx5ICRDT18yJCBlbWlzc2lvbnMgKGNvbHVtbiAiVG90YWwuQ08yLmVtaXNzaW9ucy5mcm9tLmZvc3NpbC5mdWVscy5hbmQuY2VtZW50LnByb2R1Y3Rpb24uLnRob3VzYW5kLm1ldHJpYy50b25zLm9mLkMuIikgCnN1bW1lZCBvdmVyIGFsbCBjb3VudHJpZXMgKHRoZSB3b3JsZCB0b3RhbCAkQ09fMiQgZW1pc3Npb24pLiAKWW91IGNhbiB1c2UgYSBmb3Jsb29wIG92ZXIgInllYXJzIiBvciBgZHBseXJgIGZ1bmN0aW9ucy4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeShkcGx5cikKZW1pc3Npb25zVG90YWwgPC0gZW1pc3Npb25zICU+JSAKICBncm91cF9ieShZZWFyKSAlPiUKICBzdW1tYXJpc2UoCiAgICB3b3JsZF90b3RhbCA9IAogICAgICBzdW0oVG90YWwuQ08yLmVtaXNzaW9ucy5mcm9tLmZvc3NpbC5mdWVscy5hbmQuY2VtZW50LnByb2R1Y3Rpb24uLnRob3VzYW5kLm1ldHJpYy50b25zLm9mLkMuKSkKYGBgCgoKYy4gUGxvdCB0aGUgd29ybGQgKHN1bW1lZCBvdmVyIGFsbCBjb3VudHJpZXMpICRDT18yJCBlbWlzc2lvbiBvdmVyIHRpbWUgaW4gCmJpbGxpb24gdG9ubmVzIChHdCkgcGVyIHllYXIsIGkuZS4gZGl2aWRlIHRoZSBxdWFudGl0eQpjb21wdXRlZCBpbiAoYikgYnkgMTBeNi4gWW91IGNhbiB1c2UgYSBsaW5lIG9yIGEgc2NhdHRlciBwbG90LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZyA9IEZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChlbWlzc2lvbnNUb3RhbCwgYWVzKHggPSBZZWFyLCB5ID0gd29ybGRfdG90YWwvMTBeNikpICsKICBnZW9tX2xpbmUoKSArIHRoZW1lX2J3KCkKYGBgCgpkLiBOb3cgcmVhZCB0aGUgZGF0YXNldCBsb2NhdGVkIGF0ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbHVrZXMvSVNPLTMxNjYtQ291bnRyaWVzLXdpdGgtUmVnaW9uYWwtQ29kZXMvbWFzdGVyL2FsbC9hbGwuY3N2IiB3aGljaCBjb250YWlucyBhbiBhc3NpZ25tZW50IG9mIGNvdW50cmllcyB0byByZWdpb25zLiBNZXJnZSBlbWlzc2lvbnMKZGF0YXNldCB0byB0aGUgY291bnRyaWVzIGRhdGFzZXQuIE5vdGUgdGhhdCBpbiBlbWlzc2lvbnMgZGF0YXNldCBjb3VudHJpZXMgYXJlCmdpdmVuIHdpdGggYWxsIGNhcHMsIGJ1dCBub3QgaW4gY291bnRyaWVzIGRhdGFzZXQuIFlvdSBuZWVkIHRvIGNoYW5nZSB0aGF0IGJlZm9yZQptZXJnaW5nIHRoZSB0d28gdGFibGVzLiBIaW50OiB1c2UgdGhlIGZ1bmN0aW9uIGB0b3VwcGVyKClgLiBBZGQgYSBjb2x1bW4KJ2NvMl9lbWlzc2lvbicgZXF1YWwgdG8gJENPXzIkIGVtaXNzaW9uIGluIEd0LCBpLmUuIAonVG90YWwuQ08yLmVtaXNzaW9ucy5mcm9tLmZvc3NpbC5mdWVscy5hbmQuY2VtZW50LnByb2R1Y3Rpb24uLnRob3VzYW5kLm1ldHJpYy50b25zLm9mLkMuJy8xMF42CgpgYGB7cn0KY291bnRyaWVzIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbHVrZXMvSVNPLTMxNjYtQ291bnRyaWVzLXdpdGgtUmVnaW9uYWwtQ29kZXMvbWFzdGVyL2FsbC9hbGwuY3N2IikKY291bnRyaWVzJG5hbWUgPC0gdG91cHBlcihjb3VudHJpZXMkbmFtZSkKaGVhZChjb3VudHJpZXMpCgpgYGAKCmBgYHtyfQpkZiA8LSBlbWlzc2lvbnMgJT4lIAogIGxlZnRfam9pbihjb3VudHJpZXMsIGJ5ID0gYygiTmF0aW9uIiA9ICJuYW1lIikpICU+JQogIG11dGF0ZShjbzJfZW1pc3Npb24gPSBUb3RhbC5DTzIuZW1pc3Npb25zLmZyb20uZm9zc2lsLmZ1ZWxzLmFuZC5jZW1lbnQucHJvZHVjdGlvbi4udGhvdXNhbmQubWV0cmljLnRvbnMub2YuQy4vMTBeNikKaGVhZChkZikKYGBgCgplLiBVc2UgYGRwbHlyYCB0byBjb21wdXRlIHRvdGFsIGFubnVhbCAkQ09fMiQgKCdjbzJfZW1pc3Npb24nKSAKZW1pc3Npb24gcGVyICdzdWIucmVnaW9uJy4KCgpgYGB7cn0KZGYgPC0gZGYgJT4lCiAgZ3JvdXBfYnkoWWVhciwgc3ViLnJlZ2lvbikgJT4lCiAgc3VtbWFyaXNlKGNvMl9lbWlzc2lvbiA9IHN1bShjbzJfZW1pc3Npb24pKQpoZWFkKGRmKQpgYGAKCgpmLiBVc2UgYGdncGxvdGAgdG8gZ2VuZXJhdGUgYSBzdGFja2VkIGRlbnNpdHkgcGxvdCB0aGUgYW5udWFsICRDT18yJCAKKGluIGdpZ2EgdG9ubmVzKSBieSB3b3JsZCByZWdpb25zICgic3ViLnJlZ2lvbiIpLgpZb3VyIHBsb3Qgc2hvdWxkIHJlc2VtYmxlIHNvbWV0aGluZyBsaWtlIHRoaXMgKGJ1dCB3aXRoIG90aGVyIHJlZ2lvbmFsIApjYXRlZ29yaWVzLCBhbmQgc2xpZ2h0bHkgZGlmZmVyZW50IHZhbHVlcykuIEhpbnQ6IHVzZSBgZ2VvbV9hcmVhKClgCmZ1bmN0aW9uIHdpdGggc3VpdGFibGUgcGFyYW1ldGVycy4gCgohW1NvdXJjZTogaHR0cHM6Ly9vdXJ3b3JsZGluZGF0YS5vcmcvZ3JhcGhlci9hbm51YWwtY28tZW1pc3Npb25zLWJ5LXJlZ2lvbl0oLi9hbm51YWwtY28tZW1pc3Npb25zLWJ5LXJlZ2lvbi5wbmcpCgoKYGBge3J9CmdncGxvdChkZiwgYWVzKGZpbGwgPSBzdWIucmVnaW9uLCB4ID0gWWVhciwgeSA9IGNvMl9lbWlzc2lvbikpICsKICBnZW9tX2FyZWEoc3RhdCA9ICJpZGVudGl0eSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgoKIyBFeGVyY2lzZSAyOiBHZW5lIGV4cHJlc3Npb24gZGF0YQoKSW4gdGhpcyBleGVyY2lzZSB3ZSB3aWxsIHVzZSB0aGUgRE5BIG1pY3JvYXJyYXkgZ2VuZSBleHByZXNzaW9uIGRhdGEuCllvdSBjYW4gcmVhZCBtb3JlIGFib3V0IGl0IG9uIHBhZ2UgNSBvZiBbIlRoZSBFbGVtZW50cyBvZiBTdGF0aXN0aWNhbCBMZWFybmluZyJdKGh0dHBzOi8vd2ViLnN0YW5mb3JkLmVkdS9+aGFzdGllL1BhcGVycy9FU0xJSS5wZGYpLgoKYGBge3J9Cm1pY3JvYXJyYXkgPC0gcmVhZC50YWJsZSgiaHR0cHM6Ly93ZWIuc3RhbmZvcmQuZWR1L35oYXN0aWUvRWxlbVN0YXRMZWFybi9kYXRhc2V0cy9uY2kuZGF0YSIpCmluZm8gPC0gcmVhZC50YWJsZSgiaHR0cHM6Ly93ZWIuc3RhbmZvcmQuZWR1L35oYXN0aWUvRWxlbVN0YXRMZWFybi9kYXRhc2V0cy9uY2kuaW5mby50eHQiLAogICAgICAgICAgICAgICAgICAgc2tpcCA9IDEyKQpjb2xuYW1lcyhtaWNyb2FycmF5KSA8LSBpbmZvJFYxCm1pY3JvYXJyYXlbMTo1LCAxOjVdCmBgYAoKSW4gdGhlICdtaWNyb2FycmF5JyBtYXRyaXggY29sdW1ucyBjb3JyZXNwb25kIHRvIHNhbXBsZXMsIGFuZCByb3dzIHRvIGdlbmVzLgoKYS4gUmFuZG9tbHkgc3Vic2V0IG1pY3JvYXJyYXkgdG8gNTAwIGdlbmVzLiBUaGVuLCBwbG90IGEgaGVhdG1hcCB3aXRob3V0CmNsdXN0ZXJpbmcvZGVuZHJvZ3JhbXMuIFlvdSBjYW4gdXNlICdhc3AgPSAwLjInIGFyZ3VtZW50IHRvIGNoYW5nZSB0aGUgYXNwZWN0CnJhdGlvIG9mIHRoZSBoZWF0bWFwLgoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTZ9CnNldC5zZWVkKDEyMzQ1NikKaWR4IDwtIHNhbXBsZSgxOjUwMCkKaGVhdG1hcChhcy5tYXRyaXgobWljcm9hcnJheSlbaWR4LCBdLCBSb3d2ID0gTkEsIENvbHYgPSBOQSwgYXNwPTAuMikKYGBgCgpiLiBQbG90IHRoZSBwcmV2aW91cyBoZWF0bWFwIHdpdGggcmVkL2dyZWVuIGNvbG9yIHNjaGVtZS4gRm9yIHlvdXIgY29udmVuaWVuY2UKaGVyZSBpcyB0aGUgY29sb3IgdmVjdG9yIHlvdSBtaWdodCBsaWtlIHRvIHVzZToKYGBge3IgfQpyZWRncmVlbiA8LSBjKCIjRkYwMDAwIiwgIiNEQjAwMDAiLCAiI0I2MDAwMCIsICIjOTIwMDAwIiwgIiM2RDAwMDAiLAogICAgICAgICAgICAgICIjNDkwMDAwIiwgIiMyNDAwMDAiLCAiIzAwMDAwMCIsICIjMDAyNDAwIiwgIiMwMDQ5MDAiLAogICAgICAgICAgICAgICIjMDA2RDAwIiwgIiMwMDkyMDAiLCAiIzAwQjYwMCIsICIjMDBEQjAwIiwgIiMwMEZGMDAiKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD02fQpoZWF0bWFwKGFzLm1hdHJpeChtaWNyb2FycmF5KVtpZHgsIF0sIFJvd3YgPSBOQSwgQ29sdiA9IE5BLCBhc3A9MC4yLCBjb2wgPSByZWRncmVlbikKYGBgCgpOb3csIHBsb3QgdGhlIHNhbWUgZ3JhcGggYnV0IHdpdGggZGVuZHJvZ3JhbSBmb3Igcm93cyAocm93cyBjbHVzdGVyaW5nKS4KCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTZ9CmhlYXRtYXAoYXMubWF0cml4KG1pY3JvYXJyYXkpW2lkeCwgXSwgQ29sdiA9IE5BLCBhc3A9MC4yLCBjb2wgPSByZWRncmVlbikKYGBgCgpkLiBOb3cgaW5zdGVhZCBvZiBiYXNlIGhlYXRtYXAsIHVzZSBpbnRlcmFjdGl2ZSBgaGVhdG1hcGx5KClgIHRvIGdlbmVyYXRlIHRoZSAKcHJldmlvdXMgcGxvdC4gWW91IG1pZ2h0IHdhbnQgdG8gYWRkIGEgY29tbWFuZCBzaW1pbGFyIHRvIHRoZSBmb2xsb3dpbmcgCmAgJT4lIGxheW91dChtYXJnaW4gPSBsaXN0KGwgPSAxNTAsIGIgPSAzNTApLCBhdXRvc2l6ZSA9IEYsIHdpZHRoID0gNjAwLCBoZWlnaHQgPSA4MDApYAp0byB0aGUgcGxvdCB0byBzZXQgbWFyZ2lucyBhbmQgdG8gcmVzaXplIGl0LiAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkoaGVhdG1hcGx5KQpoZWF0bWFwbHkoZGF0YS5mcmFtZShtaWNyb2FycmF5W2lkeCwgXSksIENvbHYgPSBGQUxTRSwgY29sb3JzID0gcmVkZ3JlZW4pICU+JQogIGxheW91dChtYXJnaW4gPSBsaXN0KGwgPSAxNTAsIGIgPSAzNTApLCBhdXRvc2l6ZSA9IEYsIHdpZHRoID0gNjAwLCBoZWlnaHQgPSA4MDApCmBgYAoKCmUuIFdoYXQgaW50ZXJlc3RpbmcgcGF0dGVybnMgZG8geW91IG9ic2VydmU/IEFyZSB0aGVyZQpzb21lIGRpZmZlcmVuY2VzIGJldHdlZW4gY29uZGl0aW9ucz8gQXJlIHNvbWUgZ2VuZXMgdXAgZG93biByZWd1bGF0ZWQKZm9yIGNlcnRhaW4gZ3JvdXBzPyBObyBuZWVkIGZvciBsb25nIGFuc3dlcnMganVzdCBsb29rIGF0IHRoZSBoZWF0bWFwCnN0YXRlIHdoYXQgeW91IHNlZS4KCgojIEV4ZXJjaXNlIDM6IEh5cG90aGVzaXMgdGVzdGluZwoKUmVjYWxsIHRoZSBtb3ZpZXMgZGF0YS1mcmFtZSB3ZSB1c2VkIGluIGZvciBsZWN0dXJlIDMgZXhlcmNpc2VzLiBJdCBjb250YWlucwppbmZvcm1hdGlvbiBvbiBtb3ZpZXMgZnJvbSB0aGUgbGFzdCB0aHJlZSBkZWNhdGVzLCB3aGljaCB3YXMgc2NyYXBwZWQgZnJvbQp0aGUgSU1EQiBkYXRhYmFzZS4KCmBgYHtyfQp1cmwgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9KdWFuZXRzL21vdmllLXN0YXRzL21hc3Rlci9tb3ZpZXMuY3N2Igptb3ZpZXMgPC0gdGJsX2RmKHJlYWQuY3N2KHVybCkpCm1vdmllcwpgYGAKCmEuIEdlbmVyYXRlIGEgYm94cGxvdCBvZiBydW50aW1lcyBmb3IgYWN0aW9uIG1vdmllcyBhbmQgY29tbWVkaWVzCndpdGggaml0dGVyZWQgcG9pbnRzIG92ZXJsYWlkIG9uIHRvcC4gWW91IG1pZ2h0IGNvbnNpZGVyIHNldHRpbmcgY29sbG9yLCAKZmlsbCBhbmQgYWxwaGEgYXJndW1lbnRzIHRvIG1vZGlmeSBjbGFyaXR5IGFuZCB0cmFuc3BhcmVuY3kgb2YgdGhlIHBsb3QuCgpgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KZ2dwbG90KG1vdmllcyAlPiUgZmlsdGVyKGdlbnJlICVpbiUgYygiQWN0aW9uIiwgIkNvbWVkeSIpKSwgCiAgICAgICBhZXMoeCA9IGdlbnJlLCB5ID0gcnVudGltZSkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCxhbHBoYSA9IDAuMiwgY29sb3IgPSAiZ3JleTMwIikgCmBgYAoKYi4gVGVzdCBhIGh5cG90aGVzaXMgdGhhdCB0aGUgYWN0aW9uIG1vdmllcyBoYXZlIGhpZ2hlciBtZWFuIHJ1bnRpbWUgKGxlbmd0aCkKdGhhbiB0aGUgY29tZWRpZXMuIElzIHRoZSBkaWZmZXJlbmNlIHN0YXRpc3RpY2FsbHkgZ3JlYXRlciB0aGFuIHplcm8KYXQgc2lnbmlmaWNhbmNlIGxldmVsICRcYWxwaGEgPSAwLjA1JD8KCmBgYHtyfQp0LnRlc3QoZm9ybXVsYSA9IHJ1bnRpbWUgfiBnZW5yZSwKICAgICAgIGRhdGEgPSBtb3ZpZXMgJT4lIAogICAgICAgICBmaWx0ZXIoZ2VucmUgJWluJSBjKCJBY3Rpb24iLCAiQ29tZWR5IikpLCAKICAgICAgIGFsdGVybmF0aXZlID0gImdyZWF0ZXIiKQpgYGAKCgojIEV4ZXJjaXNlIDQ6IGxpbmVhciBtb2RlbAoKYS4gUmVhZCB0aGUgZGF0YSBmcm9tICJodHRwOi8vd3d3LWJjZi51c2MuZWR1L35nYXJldGgvSVNML0FkdmVydGlzaW5nLmNzdiIKY29udGFpbmluZyBpbmZvcm1hdGlvbiBvbiBzYWxlcyBvZiBhIHByb2R1Y3QgYW5kIHRoZSBhbW91bnQgc3BlbnQgb24gYWR2ZXJ0aXNpbmcKdXNpbmcgZGlmZmVyZW50IG1lZGlhIGNoYW5uZWxzLgoKYGBge3J9CmFkdmVydGlzaW5nIDwtIHJlYWQuY3N2KCJodHRwOi8vd3d3LWJjZi51c2MuZWR1L35nYXJldGgvSVNML0FkdmVydGlzaW5nLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxKQpoZWFkKGFkdmVydGlzaW5nKQpgYGAKCmIuIEdlbmVyYXRlIGEgc2NhdHRlcnBsb3Qgb2Ygc2FsZXMgYWdhaW5zdCB0aGUgYW1vdW50IG9mIFRWIGFkdmVydGlzaW5nIGFuZCAKYWRkIGEgbGluZWFyIGZpdCBsaW5lLgoKYGBge3J9CmdncGxvdChhZHZlcnRpc2luZywgYWVzKHggPSBUViwgeSA9IHNhbGVzKSkgKwogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpCmBgYAoKYy4gTm93IG1ha2UgYSAzRCBzY2F0dGVycGxvdCB3aXRoIGF4ZXMgY29ycmVzcG9uZGluZyB0byAnc2FsZXMnLCAnVFYnCmFuZCAncmFkaW8nLgoKYGBge3J9CmxpYnJhcnkocGxvdGx5KQpwbG90X2x5KGRhdGEgPSBhZHZlcnRpc2luZywgeCA9IH5UViwgeSA9IH5yYWRpbywgeiA9IH5zYWxlcywgCiAgICAgICAgdHlwZSA9ICJzY2F0dGVyM2QiLCBtb2RlID0gIm1hcmtlcnMiLCBtYXJrZXIgPSBsaXN0KHNpemUgPSA0KSkKYGBgCgoKZC4gVGhlIGRhdGFzZXQgaGFzIDIwMCByb3dzLiBEaXZpZGUgaXQgaW50byBhIHRyYWluIHNldCB3aXRoIDE1MCBvYnNlcnZhdGlvbnMKYW5kIGEgdGVzdCBzZXQgd2l0aCA1MCBvYnNlcnZhdGlvbnMsIGkuZS4gdXNlIGBzYW1wbGUoMToyMDAsIG4gPSAxNTApYCB0bwpyYW5kb21seSBjaG9vc2Ugcm93IGluZGljZXMgb2YgdGhlIGFkdmVydGlzaW5nIGRhdGFzZXQgdG8gaW5jbHVkZSBpbiB0aGUgCnRyYWluIHNldC4gVGhlIHJlbWFpbmluZyBpbmRpY2VzIHNob3VsZCBiZSB1c2VkIGZvciB0aGUgdGVzdCBzZXQuIFJlbWVtYmVyCnRvIGNob29zZSBhbmQgc2V0IHRoZSBzZWVkIGZvciByYW5kb21pemF0aW9uIQoKYGBge3J9CnNldC5zZWVkKDEyMzQ1KQppZHggPC0gc2FtcGxlKDE6bnJvdyhhZHZlcnRpc2luZyksIDE1MCkKdHJhaW4gPC0gYWR2ZXJ0aXNpbmdbaWR4LCBdCnRlc3QgPC0gYWR2ZXJ0aXNpbmdbLWlkeCwgXQpgYGAKCgplLiBGaXQgYSBsaW5lYXIgbW9kZWwgdG8gdGhlIHRyYWluaW5nIHNldCwgd2hlcmUgdGhlIHNhbGVzIHZhbHVlcyBhcmUKcHJlZGljdGVkIGJ5IHRoZSBhbW91bnQgb2YgVFYgYWR2ZXJ0aXNpbmcuIFByaW50IHRoZSBzdW1tYXJ5IG9mIHRoZSBmaXR0ZWQgbW9kZWwuClRoZW4sIHByZWRpY3QgdGhlIHNhbGVzIHZhbHVlcyBmb3IgdGhlIHRlc3Qgc2V0IGFuZCBldmFsdWF0ZSB0aGUgdGVzdCBtb2RlbCAKYWNjdXJhY3kgaW4gdGVybXMgb2YgbWVhbiBzcXVhcmVkIGVycm9yIChNU0UpLgoKYGBge3J9CmZpdDEgPC0gbG0oc2FsZXMgfiBUViwgZGF0YSAgPSB0cmFpbikKc3VtbWFyeShmaXQxKQpgYGAKCmBgYHtyfQp5aGF0IDwtIHByZWRpY3QoZml0MSwgdGVzdCkKKG1zZTEgPC0gbWVhbigodGVzdCRzYWxlcyAtIHloYXQpXjIpKQpgYGAKCmYuIEZpdCBhIG11bHRpcGxlIGxpbmVyYXIgcmVncmVzc2lvbiBtb2RlbCBpbmNsdWRpbmcgYWxsIHRoZSB2YXJpYWJsZXMgJ1RWJywKJ3JhZGlvJywgJ25ld3NwYXBlcicgdG8gbW9kZWwgdGhlICdzYWxlcycgaW4gdGhlIHRyYWluaW5nIHNldC4gVGhlbiwgY29tcHV0ZSAKdGhlIHByZWRpY3RlZCBzYWxlcyBmb3IgdGhlIHRlc3Qgc2V0IHdpdGggdGhlIG5ldyBtb2RlbCBhbmQgZXZhbHVlZCB0aGUgTVNFLiAgCkRpZCB0aGUgZXJyb3IgZGVjcmVhc2UgZnJvbSB0aGUgb25lIGNvcnJlc3BvZG5pbmcgdG8gdGhlIHByZXZpb3VzIG1vZGVsPwoKYGBge3J9CmZpdDIgPC0gbG0oc2FsZXMgfiBUViArIHJhZGlvICsgbmV3c3BhcGVyLCBkYXRhICA9IHRyYWluKQpzdW1tYXJ5KGZpdDIpCmBgYAoKYGBge3J9CnloYXQgPC0gcHJlZGljdChmaXQyLCB0ZXN0KQoobXNlMiA8LSBtZWFuKCh0ZXN0JHNhbGVzIC0geWhhdCleMikpCmBgYAoKZy4gTG9vayBhdCB0aGUgc3VtbWFyeSBvdXRwdXQgZm9yIHRoZSBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsIGFuZCBub3RlIHdoaWNoIApvZiB0aGUgY29lZmZpY2llbnQgaW4gdGhlIG1vZGVsIGlzIHNpZ25pZmljYW50LiBBcmUgYWxsIG9mIHRoZW0gc2lnbmlmaWNhbnQ/CklmIG5vdCByZWZpdCB0aGUgbW9kZWwgaW5jbHVkaW5nIG9ubHkgdGhlIGZlYXR1cmVzIGZvdW5kIHNpZ25pZmljYW50LgpXaGljaCBvZiB0aGUgbW9kZWxzIHNob3VsZCB5b3UgY2hvb3NlPyAKCmBgYHtyfQpmaXQzIDwtIGxtKHNhbGVzIH4gVFYgKyByYWRpbywgZGF0YSAgPSB0cmFpbikKc3VtbWFyeShmaXQzKQpgYGAKCmBgYHtyfQp5aGF0IDwtIHByZWRpY3QoZml0MywgdGVzdCkKKG1zZTMgPC0gbWVhbigodGVzdCRzYWxlcyAtIHloYXQpXjIpKQpgYGAKCgojIEV4ZXJjaXNlIDQ6IGNsYXNzaWZpY2F0aW9uCkVtYWlsIHNwYW0gRVNMCgojIEV4ZXJjaXNlIDU6IGNsdXN0ZXJpbmcKCiMgRXhlcmNpc2UgNjogYGdnbWFwYAoKYS4gUGxvdCBob3VzaW5nIG1hcmtldCBwcmljZXMKYi4gUGxvdCByb3V0ZSB0byB3b3JrCgpGaXJlIHNtb2tlIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9QV0ZTTFNtb2tlL3ZpZ25ldHRlcy9NYXBzX2FuZF9UaW1lc2VyaWVzX1Bsb3RzLmh0bWwKRmlyZSBzbW9rZSBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUFdGU0xTbW9rZS92aWduZXR0ZXMvRGF0YV9Nb2RlbC5odG1sCgpodHRwczovL3d3dy5uY2Vhcy51Y3NiLmVkdS9+ZnJhemllci9SU3BhdGlhbEd1aWRlcy9nZ21hcC9nZ21hcENoZWF0c2hlZXQucGRmCgpodHRwOi8vd2ViLnN0YW5mb3JkLmVkdS9+aW1hbG9uZS9WQU0vZ2dtYXAuaHRtbAoKaHR0cDovL2VyaXFhbmRlLmdpdGh1Yi5pby9yZXAtcmVzLXdlYi9sZWN0dXJlcy9tYWtpbmctbWFwcy13aXRoLVIuaHRtbAoKaHR0cDovL3VyYmFuc3BhdGlhbGFuYWx5c2lzLmNvbS9kYXRhdml6LXR1dG9yaWFsLW1hcHBpbmctc2FuLWZyYW5jaXNjby1ob21lLXByaWNlcy11c2luZy1yLwoKaHR0cHM6Ly90YWtlb3V0Lmdvb2dsZS5jb20vc2V0dGluZ3MvdGFrZW91dAoKCmh0dHBzOi8vbWVkaXVtLmNvbS9AdGVqYXNycjE5L3Zpc3VhbGl6ZS15b3VyLWdvb2dsZS1sb2NhdGlvbi1oaXN0b3J5LWEyMzQzYjE0YTZmZQpodHRwczovL3NoaXJpbmcuZ2l0aHViLmlvL21hcHMvMjAxNi8xMi8zMC9TdGFuZG9ydHZlcmxhdWZfcG9zdAoKaHR0cHM6Ly93d3cyLndhcndpY2suYWMudWsvZmFjL3NjaS9tb2FjL3Blb3BsZS9zdHVkZW50cy9wZXRlcl9jb2NrL3IvaGVhdG1hcC8K